#ifndef _RISCV_H
#define _RISCV_H
/*
    与RISCV指令集相关的定义
    定义通用寄存器和CSR寄存器
    操作通用寄存器和CSR寄存器
*/
#ifndef __ASSEMBLER__
#include "types.h"

// M模式CSR寄存器编号
#define MSTATUS     0X300    // 机器状态寄存器
#define MEDELEG     0x302    // Machine Exception Delegation，将异常从M模式委托给其他模式
#define MIDELEG     0x303    // Machine Interrupt Delegation，将中断从M模式委托给其他模式
#define MIE         0X304    // 中断使能寄存器
#define MTVEC       0x305    // 中断向量表地址寄存器
#define MEPC        0x341    // Machine Exception Program Counter，记录中断发生时被中断指令的地址
#define MCAUSE      0x342    // 中断原因寄存器
#define MTVAL       0x343    // 中断值寄存器
#define MHARTID     0xF14    // hartid寄存器

// 机器状态寄存器相关位定义
#define MSTATUS_MPP      (3L << 11)    // 第(12:11)位表示MPP(Machine Previous Privilege)
#define MSTATUS_MPP_U    (0L << 11)    // MPP -> 00(0) 表示 User模式
#define MSTATUS_MPP_S    (1L << 11)    // MPP -> 01(1) 表示 Supervisor模式
#define MSTATUS_MPP_M    (3L << 11)    // MPP -> 11(3) 表示 Machine模式

// PMP, Physical Memory Protection
#define PMPADDR0    0x3B0    // 定义内存保护的地址范围
#define PMPCFG0     0x3A0    // 定义对应范围的访问权限

// S模式CSR寄存器编号
#define SSTATUS     0x100       // Supervisor Status, 记录当前处理器的状态
#define SIE         0x104       // Supervisor Interrupt Enable, 记录当前处理器是否支持中断
#define STVEC       0x105       // Supervisor Trap Vector, 记录当前处理器的中断处理程序入口
#define SATP        0x180       // Supervisor Address Translation and Protection
#define SEPC        0x141       // Supervisor Exception Program Counter, 记录当前处理器被中断的指令地址
#define SCAUSE      0x142       // Supervisor Exception Cause, 记录当前处理器被中断的原因
#define STVAL       0X143       // Supervisor Trap Value, 记录当前处理器被中断的具体情况

// sstatus相关位定义
#define SSTATUS_SPP      (1L << 8)    // 第8位表示SPP(Supervisor Previous Privilege)，0-U模式，1-S模式
#define SSTATUS_SIE      (1L << 1)    // 第1位表示S模式中断使能，0表示关闭，1表示开启
// sie相关位定义
#define SIE_SSIE         (1L << 1)    // 第1位表示S模式软件使能，0表示关闭，1表示开启
#define SIE_STIE         (1L << 5)    // 第1位表示S模式时钟中断使能，0表示关闭，1表示开启
#define SIE_SEIE         (1L << 9)    // 第1位表示S模式外部中断使能，0表示关闭，1表示开启
// scause值定义
#define INTR_EXTERNAL      0x8000000000000009L      // 外部中断
#define INTR_TIMER         0x8000000000000005L      // 时钟中断
#define INTR_SOFTWARE      0x8000000000000001L      // 软件中断
// satp相关位定义
#define SATP_MODE        (15L << 60)   // 第60-63位表示分页模式，0-BARE, 8-Sv39, 9-Sv48
#define SATP_MODE_SV39   (8L << 60)    // Sv39模式
// 读取CSR寄存器的值
static inline uint64 r_csr(uint64 csr) {
    unsigned long x;
    asm volatile("csrr %0, %1" : "=r"(x) : "i"(csr));
    return x;
}

// 写入CSR寄存器
static inline void w_csr(uint64 csr, uint64 x) {
    asm volatile("csrw %0, %1" : : "i"(csr), "r"(x));
}

// 读取tp寄存器的值
static inline uint64 r_tp() {
    unsigned long x;
    asm volatile("mv %0, tp" : "=r"(x));
    return x;
} 
// 写入tp寄存器
static inline void w_tp(uint64 x) {
    asm volatile("mv tp, %0" : : "r"(x));
}

static inline uint64 r_sp() {
    unsigned long x;
    asm volatile("mv %0, sp" : "=r"(x));
    return x;
}

// 关闭中断
static inline void intr_off() {
    w_csr(SSTATUS, r_csr(SSTATUS) & ~SSTATUS_SIE);
}
// 开启中断
static inline void intr_on() {
    w_csr(SSTATUS, r_csr(SSTATUS) | SSTATUS_SIE);
}

// (M模式)执行wfi指令，是当前核心进入低功耗等待状态，直到被唤醒
static inline void wfi() {
    asm volatile("wfi");
}

// 刷新TLB
static inline void sfence_vma() {
    asm volatile("sfence.vma zero, zero");
}

typedef uint64 *pagetable_t;  // 页表(512个pte)
typedef uint64 pte_t;        // 页表项

#endif
/*
    以下用于与RISCV相关的内存管理
*/
#define PGSIZE  4096        // 每页大小
// 地址按页向上对齐，-1取反按位与相当于只留下最高位
// 相当于((sz + PGSIZE) / PGSIZE) * PGSIZE
#define PGROUNDUP(sz)  ((sz+PGSIZE-1) & ~(PGSIZE-1))
// RISCV Sv39分页模式
// 采用三级页表结构，每级索引占9位，12位表示页内偏移(每页4KB)
// 最高位为符号位扩展，因此选择2 ^ 38 作为虚拟空间可用的最高地址
#define MAXVA   (1L << (9 + 9 + 9 + 12 - 1))
// PTE标志位定义
// | 63-54 | 53-10 | 9-8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
// | 保留   | PPN   | RSW | D | A | G | U | X | W | R | V |
#define PTE_V  (1L << 0)      // 第0位表示该页是否有效
#define PTE_R  (1L << 1)      // 第1位表示该页是否可读
#define PTE_W  (1L << 2)      // 第2位表示该页是否可写
#define PTE_X  (1L << 3)      // 第3位表示该页是否可执行
#define PTE_U  (1L << 4)      // 第4位表示该页是否用户可访问

// 将物理地址转换为页表项
// 右移12位去除页内偏移，左移10位留出PTE标志位
#define PA2PTE(pa)  ((((uint64)pa) >> 12) << 10)
// 将PTE转换成物理地址
// 右移10位去除PTE标志位，左移12位留出页内偏移
#define PTE2PA(pte) ((pte) >> 10 << 12)
// 取出PTE标志位(低10位)
#define PTE_FLAGS(pte) ((pte) & 0x3FF)
// 从虚拟地址取出页表索引
// | VPN[2] (9位) | VPN[1] (9位) | VPN[0] (9位) | 页内偏移 (12位) |
#define VPNMASK         0x1FF                   // 9位索引
#define VPN(va, level)  (((va) >> (12+9*(level))) & VPNMASK)

#endif